#include "Game.h"


Game::Game(void) : debugger(Debug::getInstance()), m_gameRunning(false), m_gameName("General Board Game"), m_columns(7), m_rows(7) {
	contLegalMoves = new std::vector<std::string>(0);
}

Game::Game(int columns, int rows) : debugger(Debug::getInstance()), m_gameRunning(false), m_gameName("General Board Game"), m_columns(columns), m_rows(rows) {
	contLegalMoves = new std::vector<std::string>(0);
}



Game::~Game(void)
{
	std::vector<std::string*>::iterator it;
	for( it = lastMoves.begin(); it != lastMoves.end() ; it++ ) {
		delete (*it);
	}
	delete contLegalMoves;
	debugger = 0;
}

int Game::columns() {
	return m_columns;
}

int Game::rows() {
	return m_rows;
}


bool Game::update(std::stringstream &actionStream) {

	std::string action;
	actionStream >> action;
	if(!m_gameRunning && ("start" != action && "level" != action)) {
		return false;
	}

	if("make" == action && !gameOver()) {
		int fcol,frow,tcol,trow;
		if( actionStream >> fcol >> frow >> tcol >> trow ) {
			makeMove(Move(fcol,frow,tcol,trow));
		}
	} else if ( "go" == action && !gameOver() ){
		actAi();
	} else if ("display" == action) {
		std::cout << *this;
	}
	else if("retract" == action ) {
		undoLastMove();
	}
	else if("legal" == action) {
		std::vector<std::string>* legalMoves = getLegalMoves();
		std::vector<std::string>::iterator it;
		for(it = legalMoves->begin() ; it != legalMoves->end() ; ++it ) {
			std::cout << *it << std::endl;
		}
	}else if ("level" == action) {
		std::string tempDiff;
		if( actionStream >> tempDiff) {
			if("random" == tempDiff) m_difficulty = Random;
			if("easy" == tempDiff) m_difficulty = Easy;
			if("medium" == tempDiff) m_difficulty = Medium;
			if("hard" == tempDiff) m_difficulty = Hard;
		}
		else if(debugger->debug()) {
			std::cout << "Invalid level:" << std::endl;
		}
	}
	else if("start" == action) {
		m_gameRunning = true;
		init();
	}
	
	if(winCondition()) {
		m_gameOver = true;
		int winner = evaluate();
		if(0 == winner) std::cout << "game over: tie";
		else if(0 < winner) std::cout << "game over Player1 winner";
		else std::cout << "game over Player2 winner";
		std::cout << std::endl;
	}
	legalMovesSet = false;
	return true;
}

bool Game::gameOver() {
	return m_gameOver;
}

int miniMax( Game & game, int depth ) {
	if(depth == 0 || game.gameOver()) {
		return game.evaluate();
	}
	std::vector<std::string>* legalMoves = game.getLegalMoves(true);
	int size = legalMoves->size();

	int bestValue;
	// MaxPlayer
	if(Player1 == game.currentPlayer()) {
		bestValue = INT_MIN;
		for(int i = 0 ; i < size ; ++i ) {
			game.makeMove(std::stringstream(legalMoves->at(i)));
			int temp = miniMax(game,depth-1);
			bestValue = std::max(temp,bestValue);
			game.undoLastMove();
		}
	}
	// MinPlayer
	else {
		bestValue = INT_MAX;
		for(int i = 0 ; i < size ; ++i ) {
			game.makeMove(std::stringstream(legalMoves->at(i)));
			int temp = miniMax(game,depth-1);
			bestValue = std::min(temp,bestValue);
			game.undoLastMove();
		}
	}
	delete legalMoves;
	return bestValue;
}

void Game::actAi() {
	int index = 0, size;
	int player = currentPlayer();
	std::vector<std::string>* legalMoves = getLegalMoves();
	if(0 == (size=legalMoves->size())) {
		if(debugger->debug()) std::cout << "No Legal Moves" << std::endl;
		return;
	}
	else if(Random == m_difficulty) {
		index = rand() % size;
	}
	else if(Easy == m_difficulty) {
		int score = evaluate();
		for(int i = 0 ; i < size ; ++i ) {
			makeMove(std::stringstream(legalMoves->at(i)));
			int tempScore = evaluate();
			if(Player1 == player && score < tempScore){
				index = i;
				score = tempScore;
			}else if (Player2 == player && score > tempScore) {
				index = i;
				score = tempScore;
			}
			undoLastMove();
		}
		if(debugger->debug()) std::cout << "easy: " << legalMoves->at(index) << std::endl;
	}
	else if(Medium == m_difficulty) {
		int score = evaluate();
		int tempScore;
		for(int i = 0 ; i < size ; ++i ) {
			makeMove(std::stringstream(legalMoves->at(i)));
			
			tempScore = miniMax(*this,2);
			if(Player1 == player && tempScore > score ){
				index = i;
				score = tempScore;
			}else if (Player2 == player && score > tempScore) {
				index = i;
				score = tempScore;
			}
			undoLastMove();
		}
	}
	else if(Hard == m_difficulty) {
		if(debugger->debug())std::cout << "Hard not implemented" << std::endl;
		return;
	}
	else {
		return;
	}

	makeMove(std::stringstream( legalMoves->at(index) ));
}



void Game::init() {
	m_difficulty = 0;
	m_gameRunning = true;
	m_currentPlayer = Player1;
	legalMovesSet = false;

	srand(static_cast<int>(time(NULL)));

	AI = false;
	m_gameOver = false;
	totalTurns = 0;
	lastMoves.clear();
	// TODO more initialization
	placePieces();

	// Changes members m_player1Pieces,m_player2Pieces
	m_gameBoard.countPieces(m_player1Pieces,m_player2Pieces);
}

bool Game::winCondition() {
	if(MAX_TURNS == totalTurns) {
		return true;
	}
	return false;
}

int Game::evaluate() {
	return m_player1Pieces - m_player2Pieces;
}

std::ostream& operator<<(std::ostream& os , Game& game) {
	os << game.gameBoard();
	os << game.currentPlayer() << std::endl;
	os << game.getPlayerPiecesCount( Player1 );
	os << " ";
	os << game.getPlayerPiecesCount( Player2 ) << std::endl;
	return os;
}

Board& Game::gameBoard() {
	return m_gameBoard;
}

int Game::currentPlayer() {
	return m_currentPlayer;
}

bool Game::checkLegal(const Move &gameMove) {
	bool invalid = false;
	std::string error;

	int pieceOwner = gameBoard().getOccupancy(gameMove.f_col,gameMove.f_row);
	if(None == pieceOwner) {
		invalid = true;
		if(!AI)error = "Cannot move an empty field";

	}
	else if(((gameMove.t_col < 1) || (gameMove.t_col > gameBoard().getColumnSize())) 
		|| ((gameMove.t_row < 1) || (gameMove.t_row > gameBoard().getRowSize())) ) {
			invalid = true;
			if(!AI)error = "destination out of board region";
	}
	// TODO: Should derived game handle obstructed fields?
	// Game piece of certain type could destroy

	// Fail if Field is Unavailable
	else if(Unavailable == gameBoard().getOccupancy(gameMove.t_col,gameMove.t_row)) {
		invalid = true;
		if(!AI)error = "destination unavailable";
	}
	if(invalid) {
		if(!AI) std::cout << "Illegal move:" << std::endl; 
		if(debugger->debug() && !AI) {
			std::cout << error << std::endl; 
		}
		return false;
	}
	// If game move is valid, check for move legality from derived class.
	return legalMove(gameMove);
}



void Game::makeMove(const Move &gameMove) {
	if(checkLegal(gameMove)) {
		std::stringstream action;

		constructMoveString(gameMove,action);

		//lastMoves.push_back(new std::string(action.str()));
		makeMove(action);
		//doMakeMove(action);
		//++totalTurns;

	}
	else {
		// TODO error Handling.
		if(debugger->debug()) {
			std::cout << "Illegal Move: " << std::endl <<  gameMove.display() << std::endl;
		}
		return;
	}
}

void Game::makeMove(  std::stringstream &gameMove ) {
	lastMoves.push_back(new std::string(gameMove.str()));
	doMakeMove(gameMove);
	++totalTurns;
}

void Game::doMakeMove(std::stringstream &actionStream) {
	if(debugger->debug()) {
		std::cout << actionStream.str() << std::endl;
	}
	
	std::string action;
	actionStream >> action;
	int victim;
	// <action>:: <attack> | "Move "
	// <attack>:: "Attack" <victim>
	// <victim>:: Player1 | Player2
	if("Attack" == action) {
		actionStream >> victim;
	}
	int f_col,f_row,t_col,t_row;
	actionStream >> f_col >> f_row;
	actionStream >> t_col >> t_row;
	Move gameMove(f_col,f_row,t_col,t_row);
	gameBoard().movePiece(gameMove);

	doGameSpecialAction(actionStream,gameMove);

	if( Player1 == currentPlayer() ) {
		m_currentPlayer = Player2; 
	}
	else {
		m_currentPlayer = Player1;
	}
	// Note: Countpieces changes parameters.
	gameBoard().countPieces(m_player1Pieces,m_player2Pieces);
}



int Game::getPlayerPiecesCount(int player) {
	if(Player1 == player) return m_player1Pieces;
	if(Player2 == player) return m_player2Pieces;
	// TODO handle error.
	return -1;
}

void Game::undoLastMove() {
	if(!lastMoves.empty() ) { 
		std::stringstream actionStr;
		std::stringstream undoAction;
		actionStr.str(*(lastMoves.back()));
		delete lastMoves.back();
		lastMoves.pop_back();
		m_gameOver = false;

		// Reconstruct the Move from the lastMoves vector.
		int f_col,f_row,t_col,t_row;
		std::string action;
		actionStr >> action;
		undoAction << action << " ";
		int victim = -1;
		if("Attack" == action) {
			actionStr >> victim;
			if(Player1 == victim) {
				undoAction << Player2 << " ";
			}else {
				undoAction << Player1 << " ";
			}
		}
		actionStr >> t_col >> t_row >> f_col >> f_row;
		Move undoMove(f_col,f_row,t_col,t_row);
		undoAction << undoMove << " ";
		// TODO: OJJ
		int gleip;
		actionStr >> gleip;
		// TODO: OJJ actionStr >> m_currentPlayer;

		undoAction << currentPlayer() << " ";
		undoSpecialAction(undoAction, actionStr);
	
		doMakeMove(undoAction);
		--totalTurns;

		if(debugger->debug()) {
			std::cout << "Undoing " << action << ": " << std::endl;
			std::cout << undoMove.display()<< std::endl;
		}

		// replaces lost piece if action was attack.
		if(victim != -1) {
			if(Player1 == victim) {
				m_player1Pieces++;
			}
			else if(Player2 == victim) {
				m_player2Pieces++;
			}
			gameBoard().placePiece(f_col,f_row,victim);
		}

	} else {
		std::cout << "Nothing to undo." << std::endl;
	}
}

void Game::constructMoveString(const Move &gameMove, std::stringstream& action ) {
	int targetOccupuyer = gameBoard().getOccupancy(gameMove.t_col,gameMove.t_row);
	if(None == targetOccupuyer) {
		action << "Move ";
	} else {
		action << "Attack ";
		action << targetOccupuyer << " ";
	}
	action << gameMove << " ";
	action << currentPlayer() << " ";
	action << gameSpecialAction(gameMove);
}

std::vector<std::string>* Game::getLegalMoves( bool unique ) {
	std::vector<std::string>* result;
	
	if(!legalMovesSet || unique ) {
		result = new std::vector<std::string>();
		AI = true;
		std::vector<std::pair<int,int> > possibleDestinations;
		std::vector<std::pair<int,int> > pieces;
		std::stringstream stream;
		Move tempMove;

		gameBoard().getPlayerPieces(currentPlayer(),pieces);
		std::vector<std::pair<int,int> >::iterator it;
		for( it = pieces.begin() ; it != pieces.end() ; ++it ) {
			getPossibleDestinations(possibleDestinations,*it);
			std::vector<std::pair<int,int> >::iterator destIt;
			for (destIt = possibleDestinations.begin() ; destIt != possibleDestinations.end() ; ++destIt ) {
				tempMove.set(it->first,it->second,destIt->first,destIt->second);
				if(checkLegal(tempMove)) {
					constructMoveString(tempMove,stream);
					result->push_back(std::string(stream.str()));
					stream.str("");
					stream.clear();
				}
			}

		}
		if(!legalMovesSet) {
			contLegalMoves = result;
			legalMovesSet = true;
		}

		AI = false;
	} 
	if(unique) {
		return result;
	}

	return contLegalMoves;
}